function AutoCompleteTextbox(textboxID, url) {
	this.fontName = "Arial";
	this.fontSize = "11px";
	this.textAlign = "Left";
	this.tableID = "popupTable" + textboxID;
	this.selectedColor = "#C0DCC0";   
	this.backgroundColor = "#CFCFCF";
	this.foregroundColor = "#000000";
	
	this.url = url;
	this.topBoundaryIndex = 0;
	this.visibleItemCount = 7;
	this.relativeCurrentIndex = 0;
	
	this.validItems = new Array();
	this.textbox = document.getElementById(textboxID);
	this.textbox.AutoCompleteTextbox = this;
	this.textbox.onkeyup = function(evt) {
		var crossEvent = (evt)? evt : event;
		var keyCode = crossEvent.keyCode;
		if(keyCode == 38 || keyCode == 40 || keyCode == 13) return;
		this.AutoCompleteTextbox.doOrdinaryKeyAction(crossEvent);
	}
	this.textbox.onkeydown = function(evt) {
		var crossEvent = (evt)? evt : event;
		var keyCode = crossEvent.keyCode;
		if(keyCode == 38) this.AutoCompleteTextbox.doKeyUpAction(crossEvent);
		else if(keyCode == 40) this.AutoCompleteTextbox.doKeyDownAction(crossEvent);
		else if(keyCode == 13) this.AutoCompleteTextbox.doEnterKeyAction(crossEvent);
		else this.AutoCompleteTextbox.doOrdinaryKeyAction(crossEvent);
	}
	this.textbox.onblur = function() {
		this.AutoCompleteTextbox.disposeTable();
	}
}

AutoCompleteTextbox.prototype.showTable = function(visibleItems) {	
	this.disposeTable();
	var table = document.createElement("TABLE");
	table.id = this.tableID;
	table.style.backgroundColor = this.backgroundColor;
	table.style.position = "absolute";
	table.style.cursor = "default"; // {pointer, text, auto, default, move, wait}
	table.style.top = (this.textboxTop() + this.textbox.offsetHeight) + "px";
	table.style.left = this.textboxLeft() + "px";
	table.style.width = this.textbox.offsetWidth + "px";
	
	for(var i = 0; i < visibleItems.length; i++) {
		var row = table.insertRow(-1);
		row.style.backgroundColor = (i == this.relativeCurrentIndex)? this.selectedColor : this.backgroundColor;
		var cell = row.insertCell(-1);
		cell.id = i;
		cell.style.fontSize = this.fontSize;
		cell.style.textAlign = this.textAlign;
		cell.style.fontFamily = this.fontName;
		cell.style.color = this.foregroundColor;
		cell.innerHTML = visibleItems[i];
		cell.AutoCompleteTextbox = this;
		cell.onmousemove = function() {
			this.AutoCompleteTextbox.relativeCurrentIndex = parseInt(this.id);
			this.AutoCompleteTextbox.apply();
		}
		cell.onmousedown = function() {
			this.AutoCompleteTextbox.relativeCurrentIndex = parseInt(this.id);
			this.AutoCompleteTextbox.doEnterKeyAction();
		}
	}
	document.body.appendChild(table);
}

AutoCompleteTextbox.prototype.doKeyUpAction = function() {
	if(!this.tableIsVisible()) return;
	if(this.relativeCurrentIndex == 0 && this.topBoundaryIndex > 0) this.topBoundaryIndex--;
	else if(this.relativeCurrentIndex > 0) this.relativeCurrentIndex--;
	this.apply();
}

AutoCompleteTextbox.prototype.doKeyDownAction = function() {
	if(!this.tableIsVisible()) return;
	var limitCount = Math.min(this.validItems.length, this.visibleItemCount);
	if(this.relativeCurrentIndex == limitCount - 1) {
		if(this.topBoundaryIndex + this.visibleItemCount < this.validItems.length) this.topBoundaryIndex++;
	}
	else if(this.relativeCurrentIndex < limitCount) this.relativeCurrentIndex++;
	this.apply();
}

AutoCompleteTextbox.prototype.doEnterKeyAction = function() {
	if(this.tableIsVisible()) this.textbox.value = this.validItems[this.topBoundaryIndex + this.relativeCurrentIndex];
	this.disposeTable();
}

AutoCompleteTextbox.prototype.doOrdinaryKeyAction = function() {
	var request = createAjaxRequest();
	request.open('GET', encodeURI(this.url + '?' + this.textbox.getAttribute('param') + '=' + this.textbox.value + '&nocache=' + new Date().getTime()), true);
	var actb = this;
	request.onreadystatechange = function() {
		if(request.readyState == 4 && request.status == 200) {
			actb.rebuild();
			if(actb.textbox.value.length == 0) {
				actb.disposeTable();
			}
			var items = request.responseXML.getElementsByTagName("items")[0];
			for(var i = 0; i < items.childNodes.length; i++) {
				actb.validItems.push(items.childNodes[i].firstChild.nodeValue);
			}
			actb.apply();
		}
	}
	request.send(null);	
}

AutoCompleteTextbox.prototype.rebuild = function() {
	this.disposeTable();
	this.validItems = new Array();
	this.relativeCurrentIndex = 0;
	this.topBoundaryIndex = 0;
}

AutoCompleteTextbox.prototype.apply = function() {
	var limitIndex = Math.min(this.validItems.length, this.visibleItemCount + this.topBoundaryIndex);
	var visibleItems = new Array();
	for(var index = this.topBoundaryIndex; index < limitIndex; index++)
		visibleItems.push(this.validItems[index]);
	this.showTable(visibleItems);
}

AutoCompleteTextbox.prototype.tableIsVisible = function() {
	return document.getElementById(this.tableID) != null;
}

AutoCompleteTextbox.prototype.disposeTable = function() {
	if(this.tableIsVisible()) document.body.removeChild(document.getElementById(this.tableID));
}

AutoCompleteTextbox.prototype.textboxTop = function() {
    var top = 0;
    var currentElement = this.textbox;
    while(currentElement) {
        top += currentElement.offsetTop;
        currentElement = currentElement.offsetParent;
    }
    return top;
}

AutoCompleteTextbox.prototype.textboxLeft = function() {
    var left = 0;
    var currentElement = this.textbox;
    while(currentElement) {
        left += currentElement.offsetLeft;
        currentElement = currentElement.offsetParent;
    }
    return left;
}

<!-- Cross Browser Utils -->

function createAjaxRequest() {
	if (typeof XMLHttpRequest != "undefined") {
		return new XMLHttpRequest();
	} 
	else if (window.ActiveXObject) {
		var msVersions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0"];
		for (var i = 0; i < msVersions.length; i++) {
			try {
				return new ActiveXObject(msVersions[i]);
			} 
			catch (ex) {}
		}
	}
	throw new Error("XMLHttpRequest is not supported by the browser");
}